package com.wlz.oauth2.config;

import com.wlz.oauth2.enhancer.JwtTokenEnhancer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import java.util.ArrayList;
import java.util.List;

/**
 *  配置授权服务器
 *     基于内存配置
 *
 *    实现 sso
 *      1. 创建客户端
 *          详情 进入  spring-oauth2-sso-client 模块查看
 *
 *      2. 配置：
 *          1.修改授权服务器配置类 （将绑定的跳转路径为http://localhost:8081/login (模拟多个客户端可配置多个)，并添加获取秘钥时的身份认证）
 *            1)  com.wlz.oauth2.config.AuthorizationServerJwtSSOConfig#configure(org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer)
 *                   // 获取密钥需要身份认证，使用单点登录时必须配置
 *                   .tokenKeyAccess("isAuthenticated()")
 *            2) com.wlz.oauth2.config.AuthorizationServerJwtSSOConfig#configure(org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer)
 *                 .redirectUris("http://localhost:8081/login","http://localhost:8082/login")
 *                 //自动授权配置
 *                 .autoApprove(true)
 *
 *       3. 测试:
 *          1) 启动授权服务和客户端服务；
 *              com.wlz.oauth2.SecurityOauth2JwtApplication
 *              com.wlz.sso.Oauth2SSOClientApplication      8081
 *              com.wlz.sso.Oauth2SSOClientApplication      8082
 *          2) 访问客户端需要授权的接口
 *              http://localhost:8081/user/getCurrentUser
 *          3) 会跳转到授权服务的登录界面
 *          4) 授权后会跳转到原来需要权限的接口地址(http://localhost:8081/user/getCurrentUser)，展示登录用户信息
 *
 *          5) 访问 http://localhost:8082/user/getCurrentUser 接口，会发现不用登录就可以访问 （8081登录成功之后，8082无需再次登录就可以访问）
 *
 * @author wlz
 * @desc
 * @date 2021-10-05 14:39
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerJwtSSOConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private AuthenticationManager authenticationManagerBean;

    @Autowired
    private UserDetailsService myUserDetailsService;

    @Autowired
    private TokenStore jwtTokenStore;

    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    @Autowired
    private JwtTokenEnhancer jwtTokenEnhancer;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //配置JWT的内容增强器
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> delegates = new ArrayList<>();
        delegates.add(jwtTokenEnhancer);
        delegates.add(jwtAccessTokenConverter);
        enhancerChain.setTokenEnhancers(delegates);
        endpoints.authenticationManager(authenticationManagerBean) // 使用密码模式需要配置
                .tokenStore(jwtTokenStore) // 配置存储令牌策略
                .accessTokenConverter(jwtAccessTokenConverter) // 配置
                .reuseRefreshTokens(false) // refresh_token是否重复使用
                .userDetailsService(myUserDetailsService) // 刷新令牌授权包含对用户信息的检查
        .allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST); // 支持 GET POST 请求
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.allowFormAuthenticationForClients() // 允许表单登陆认证
        // 获取密钥需要身份认证，使用单点登录时必须配置
        .tokenKeyAccess("isAuthenticated()");
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

        //刷新令牌
        // http://localhost:8080/oauth/token?grant_type=refresh_token&client_id=client&client_secret=123456&refresh_token=de9c7723-78cb-4a01-95b5-0cc4c497e2b8

        clients.inMemory()
                .withClient("client") // 配置 client_id
                .secret(passwordEncoder.encode("123456")) // 配置 client_secret = $2a$10$q96KQOrMpn82Fyej9oB.n.S.T4.gAEUEtQNvtEgxd36Nguc09w3VS
                .accessTokenValiditySeconds(3600) // 配置访问token的有效期
                .refreshTokenValiditySeconds(864000) // 配置刷新token的有效期
//                .redirectUris("https://www.baidu.com") // 配置redirect_uri，用于授权成功后跳转
                .redirectUris("http://localhost:8081/login",
                        "http://localhost:8082/login")
                //自动授权配置
                .autoApprove(true)
                .scopes("all")  // 配置申请的权限范围
                // 配置grant_type，表示授权类型 authorization_code: 授权码模式; implicit: 简化模式; password: 密码模式; client_credentials：客户端模式；refresh_token:刷新令牌;
                .authorizedGrantTypes("authorization_code", "implicit","password","client_credentials","refresh_token");
    }
}
